package com.xr.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 反射工具类（JavaBean 与 Map互相转换）
 *
 * @author XR FZH
 */
@Slf4j
public class XrJavaBeanUtil {

    /**
     * JavaBean对象转换为Map对象
     *
     * @param object 被转换的JavaBean对象
     * @return map   Map对象
     */
    public static Map<?, ?> convertObjectToMap(Object object) {
        if (ObjectUtils.isEmpty(object)) {
            return null;
        }
        Map<String, Object> map = new HashMap<>();
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();
                if (!key.equals("class")) {
                    Method getter = property.getReadMethod();
                    Object value = getter.invoke(object);
                    map.put(key, value);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * Map对象转换为JavaBean对象
     * 被转换的Map对象的转换目标JavaBean未继承父类（有一定局限性，JavaBean未继承父类时可用）
     *
     * @param map   被转换的Map对象
     * @param clazz 实体类对象（如User.class）
     * @param <K>   实体类泛型（如User）
     * @return k    JavaBean对象
     */
    public static <K> K convertMapToJavaBean(Map<String, Object> map, Class<K> clazz) {
        if (ObjectUtils.isEmpty(map)) {
            return null;
        }
        K k = null;
        try {
            k = clazz.newInstance();
            for (Field field : clazz.getDeclaredFields()) {
                if (map.containsKey(field.getName())) {
                    field.setAccessible(true);
                    Object object = map.get(field.getName());
                    if (object != null && field.getType().isAssignableFrom(object.getClass())) {
                        if (field.getType() == Integer.class) {
                            field.set(object, Integer.valueOf(map.get(field.getName()).toString()));
                        } else if (field.getType() == Long.class) {
                            field.set(object, Long.valueOf(map.get(field.getName()).toString()));
                        } else if (field.getType() == String.class) {
                            field.set(object, map.get(field.getName()).toString());
                        } else if (field.getType() == Double.class) {
                            field.set(object, Double.valueOf(map.get(field.getName()).toString()));
                        } else if (field.getType() == Float.class) {
                            field.set(object, Float.valueOf(map.get(field.getName()).toString()));
                        } else if (field.getType() == Boolean.class) {
                            field.set(object, Boolean.valueOf(map.get(field.getName()).toString()));
                        } else if (field.getType() == Date.class) {
                            String dateString = map.get(field.getName()).toString();
                            Date date = convertDateStringToDate(dateString);
                            field.set(object, date);
                        } else if (field.getType() == LocalDateTime.class) {
                            String localDateTimeString = map.get(field.getName()).toString();
                            LocalDateTime localDateTime = convertLocalDateTimeStringToDate(localDateTimeString);
                            field.set(object, localDateTime);
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return k;
    }

    /**
     * Map对象转换为JavaBean对象
     * 被转换的Map对象的转换目标JavaBean继承了父类，为保证不会丢失其父类属性，先获取父类属性然后赋值给子类
     *
     * @param map   被转换的Map对象
     * @param clazz 实体类对象（如User.class）
     * @return object 转换后得到的Object对象（需强转为对应的JavaBean对象）
     */
    public static Object convertMapToObject(Map<?, ?> map, Class<?> clazz) {
        if (ObjectUtils.isEmpty(map)) {
            return null;
        }
        Object object = null;
        try {
            object = clazz.newInstance();
            Class<?> c = object.getClass();
            List<Field> fields = new ArrayList<>();
            while (null != c) {
                fields.addAll(Arrays.asList(c.getDeclaredFields()));
                c = c.getSuperclass();
            }
            for (Field field : fields) {
                int mod = field.getModifiers();
                if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
                    continue;
                }
                field.setAccessible(true);
                if (field.getType() == Integer.class) {
                    field.set(object, Integer.valueOf(map.get(field.getName()).toString()));
                } else if (field.getType() == Long.class) {
                    field.set(object, Long.valueOf(map.get(field.getName()).toString()));
                } else if (field.getType() == String.class) {
                    field.set(object, map.get(field.getName()).toString());
                } else if (field.getType() == Double.class) {
                    field.set(object, Double.valueOf(map.get(field.getName()).toString()));
                } else if (field.getType() == Float.class) {
                    field.set(object, Float.valueOf(map.get(field.getName()).toString()));
                } else if (field.getType() == Boolean.class) {
                    field.set(object, Boolean.valueOf(map.get(field.getName()).toString()));
                } else if (field.getType() == Date.class) {
                    String dateString = map.get(field.getName()).toString();
                    Date date = convertDateStringToDate(dateString);
                    field.set(object, date);
                } else if (field.getType() == LocalDateTime.class) {
                    String localDateTimeString = map.get(field.getName()).toString();
                    LocalDateTime localDateTime = convertLocalDateTimeStringToDate(localDateTimeString);
                    field.set(object, localDateTime);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return object;
    }

    private static Date convertDateStringToDate(String dateString) {
        SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US);
        Date date = null;
        try {
            date = sdf.parse(dateString);
        } catch (Exception e) {
            log.error("解析错误，日期格式不合法！");
            e.printStackTrace();
        }
        return date;
    }

    private static LocalDateTime convertLocalDateTimeStringToDate(String localDateTimeString) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
        LocalDateTime localDateTime = null;
        try {
            localDateTime = LocalDateTime.parse(localDateTimeString, df);
        } catch (Exception e) {
            log.error("解析错误，日期格式不合法！");
            e.printStackTrace();
        }
        return localDateTime;
    }

}
